package ink.metoo.gude.module.websocket.service.impl

import com.fasterxml.jackson.databind.ObjectMapper
import com.fasterxml.jackson.module.kotlin.readValue
import ink.metoo.gude.model.TopicConst
import ink.metoo.gude.module.security.util.simpleUserDetails
import ink.metoo.gude.module.websocket.domain.ddl.Message
import ink.metoo.gude.module.websocket.domain.dto.HistoryMessageDTO
import ink.metoo.gude.module.websocket.domain.dto.MessageDTO
import ink.metoo.gude.module.websocket.domain.model.TextMessage
import ink.metoo.gude.module.websocket.domain.vo.PrivateHistoryMessageVO
import ink.metoo.gude.module.websocket.domain.vo.PublicMessageVO
import ink.metoo.gude.module.websocket.repository.MessageRepository
import ink.metoo.gude.module.websocket.service.BotService
import ink.metoo.gude.module.websocket.service.MessageService
import ink.metoo.gude.util.SensitiveWordUtils
import org.springframework.data.domain.Page
import org.springframework.data.domain.Pageable
import org.springframework.data.domain.Sort
import org.springframework.data.jpa.domain.Specification
import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder
import org.springframework.messaging.simp.SimpMessagingTemplate
import org.springframework.security.core.Authentication
import org.springframework.stereotype.Service
import org.springframework.transaction.annotation.Transactional
import kotlin.math.min

@Service
class MessageServiceImpl(
    private val botService: BotService,
    private val messageTemplate: SimpMessagingTemplate,
    private val messageRepository: MessageRepository,
    jackson2ObjectMapperBuilder: Jackson2ObjectMapperBuilder,
) : MessageService {

    val objectMapper: ObjectMapper = jackson2ObjectMapperBuilder.build()

    @Transactional
    override fun doMessage(message: MessageDTO, token: Authentication) {
        val details = token.simpleUserDetails!!
        val content = message.objectContent
        var msg = Message()
        msg.type = message.type
        msg.content = objectMapper.writeValueAsString(content)
        msg.creator = details.user
        msg = messageRepository.save(msg)
        val payload = PublicMessageVO()
        payload.fromMessage(msg)
        payload.content = objectMapper.readValue<Map<String, Any>>(msg.content.toString())
        messageTemplate.convertAndSend(TopicConst.PUBLIC_TOPIC, payload)
        when (content) {
            is TextMessage -> {
                botService.doCommand(token.name!!, content.text)
            }
        }
    }

    @Transactional
    override fun doHistoryMessage(dto: HistoryMessageDTO, token: Authentication) {
        val mid = dto.mid
        var specification = Specification.unrestricted<Message>()
        if (mid != null) {
            specification = specification.and { root, query, cb ->
                cb.lt(root.get("id"), mid)
            }
        }
        val page: Page<Message> = messageRepository.findBy(specification) { query ->
            query.sortBy(Sort.by("id").descending()).page(Pageable.ofSize(dto.size))
        }

        val vo = PrivateHistoryMessageVO()
        vo.hasMore = page.hasNext()
        vo.messages = page.content.map {
            val payload = PublicMessageVO()
            payload.fromMessage(it)
            vo.lastMId = min(it.id ?: -1, vo.lastMId ?: -1)
            payload.content = objectMapper.readValue<Map<String, Any>>(it.content.toString())
            payload
        }.sortedBy { it.mid }

        messageTemplate.convertAndSendToUser(token.name!!, TopicConst.PRIVATE_TOPIC, vo)
    }

    /**
     * 处理消息内容
     */
    val MessageDTO.objectContent: Any?
        get() {
            when (type) {
                Message.Type.TEXT -> {
                    val textMessage = objectMapper.readValue<TextMessage>(content)
                    textMessage.text = SensitiveWordUtils.replace(textMessage.text)
                    return textMessage
                }

                else -> {
                    return null
                }
            }
        }


}